<?php

namespace lico\Tool\Encrypt;

class Openssl
{

    //命令行模式：
    //生成原始 RSA私钥文件 rsa_private_key.pem
    //openssl genrsa -out rsa_private_key.pem 1024

    //生成 RSA公钥 rsa_public_key.pem
    //openssl rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem

    public function __construct()
    {
        extension_loaded('openssl') or die('php需要openssl扩展支持');
        $this->init();
    }

    /**
     * 配置
     */
    protected $config = [
        'privateKeyPath' => __DIR__ . DS . 'rsa_private_key.pem',
        'publicKeyPath' => __DIR__ . DS . 'rsa_public_key.pem',
        'privateKey' => '',
        'publicKey' => '',
        'padding' => OPENSSL_PKCS1_PADDING, //填充方式
    ];

    public function setConfig($config)
    {
        $this->config = array_merge($this->config, $config);
        return $this->config;
    }

    /**
     * 获取秘钥
     */
    public function init()
    {
        if (!$this->config['privateKey'] && !$this->config['publicKey']) {
            $rsaPrivateKeyPath = $this->config['privateKeyPath'];
            $rsaPublicKeyPath = $this->config['publicKeyPath'];

            if (!file_exists($rsaPrivateKeyPath) && !file_exists($rsaPublicKeyPath)) {
                $this->createKey($rsaPrivateKeyPath, $rsaPublicKeyPath);
            }

            if (empty($this->config['privateKey'])) {
                $privateContent = file_get_contents($rsaPrivateKeyPath);
                $this->config['privateKey'] = openssl_pkey_get_private($privateContent);
            }

            if (empty($this->config['publicKey'])) {
                $publicContent = file_get_contents($rsaPublicKeyPath);
                $this->config['publicKey'] = openssl_pkey_get_public($publicContent);
            }
        }
    }

    /**
     * 创建秘钥
     */
    protected function createKey($privatePath, $publicPath)
    {

        $config = [
            "digest_alg" => "sha512", //算法或签名哈希算法
            "private_key_bits" => 4096, //指定应该使用多少位来生成私钥  512 1024  2048  4096等
            "private_key_type" => OPENSSL_KEYTYPE_RSA,
        ];
        // 生成私钥
        $rsa = openssl_pkey_new($config);
        if (!$rsa) {
            die('生成秘钥失败，请检查是否存在openssl.cnf,或者手动生成秘钥文件');
        }
        openssl_pkey_export($rsa, $privateKey, null, $config);
        file_put_contents($privatePath, $privateKey);

        // 生成公钥
        $rsaPri = openssl_pkey_get_details($rsa);
        $pubKey = $rsaPri['key'];
        file_put_contents($publicPath, $pubKey);

        return true;
    }

    /**
     * openssl  签名
     * string $data  待签名字符串
     * 对要传输的数据做一个数字签名，在接收端对接收到的数据进行一个签名运算，
     * 只要客户端计算的签名和接受的的签名一样就可以认为收到的数据没有被篡改过。
     */
    public function sign($data)
    {

        // 进行签名
        openssl_sign($data, $sign, $this->config['privateKey']);
        // 为了防止乱码对签名结果进行base64转码
        $sign = base64_encode($sign);

        return $sign;
    }

    /**
     * openssl  签名校验
     * data string 待校验的签名字符串
     * sign string 签名字符串，由上面sign获取的
     */
    public function verify($data, $sign)
    {

        // 因为我这上面是进行了base64转码的，所以这里要先解码
        $result = openssl_verify($data, base64_decode($sign), $this->config['publicKey']);

        return $result;
    }

    /**
     * 加密
     * 非对称加密
     * 私钥加密
     */
    public function encript($data)
    {

        $success = openssl_private_encrypt($data, $encryptData, $this->config['privateKey'], $this->config['padding']);
        if ($success) {
            $result = base64_encode($encryptData);
        } else {
            $result = [];
        }

        return $result;
    }

    /**
     * 解密
     * 非对称解密
     * 公钥解密
     */
    public function decript($encryptData)
    {

        $encryptData = base64_decode($encryptData);

        $success = openssl_public_decrypt($encryptData, $decryptData, $this->config['publicKey'], $this->config['padding']);
        if ($success) {
            $result = $decryptData;
        } else {
            $result = [];
        }
        return $result;
    }
}
